/***************************/
/* custom global functions */
/***************************/

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function()
{
    var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
    // The base Class implementation (does nothing) - "this" is the window object (in this context)
    this.Class = function(){};

    // Create a new Class that inherits from this class
    Class.extend = function( prop )
    {
        var _super = this.prototype;

        // Instantiate a base class (but only create the instance,
        // don't run the init constructor)
        initializing = true;
        var prototype = new this();
        initializing = false;

        // Copy the properties over onto the new prototype
        for ( var name in prop )
        {
            // Check if we're overwriting an existing function
            prototype[name] =
                typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ?
                (function( name, fn )
                {
                    return function()
                    {
                        var tmp = this._super;

                        // Add a new ._super() method that is the same method
                        // but on the super-class
                        this._super = _super[name];

                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;

                        return ret;
                    };
                })(name, prop[name]) :

                prop[name];
        }

        // The dummy class constructor
        function Class()
        {
            // All construction is actually done in the init method
            if ( !initializing && this.init )
            {
                this.init.apply( this, arguments );
            }
        }

        // Populate our constructed prototype object
        Class.prototype = prototype;

        // Enforce the constructor to be what we expect
        Class.prototype.constructor = Class;

        // And make this class extendable
        Class.extend = arguments.callee;

        return Class;
    };
})();

/**
 * Takes an element and defines special functionality
 * The derived classes will define the "_init" method as their sub-constructor; it is recommended to not override the 'init' method
 * The constructor takes 2 params:
 *  - the element as jQuery obeject
 *  - an optional object with several options
 * The methods starting with '_init' are automatically executed; the '_init()' method is executed first
 * The init methods are passed no arguments
 */
var Module = Class.extend(
{
    _$element: null,
    _timestamp: null,
    _options: {},
    _prefixConstructor: '_init',

    init: function( $element, options )
    {
        // save the element
        this._$element = $element;

        // override default options
        $.extend( this._options, options );

        // save this module in the 'data' storage area
        this._$element.data( 'module', this );

        // save a timestamp for the current object
        this._timestamp = (new Date()).valueOf();

        // call the sub-constructor '_init' and other methods that start with '_init'
        var prefix = this._prefixConstructor;
        if ( $.isFunction( this[prefix] ) )
        {
            this[prefix].call( this );
        }
        for ( var method in this )
        {
            if ( $.isFunction( this[method] )
                && method.indexOf( prefix ) == 0
                && method.length > prefix.length )
            {
                this[method].call( this );
            }
        }

    },

    getElement: function()
    {
        return this._$element;
    },

    getTimestamp: function()
    {
        return this._timestamp;
    },

    bind: function( types, data, fn )
    {
        this.getElement().bind( types, data, fn );
    },

    unbind: function( types, fn )
    {
        this.getElement().unbind( types, fn );
    },

    trigger: function( type, data )
    {
        this.getElement().trigger( type, data );
    },

    getOption: function( option )
    {
        return this._options[ option ];
    },

    setOption: function( option, value )
    {
        this._options[ option ] = value;
    },

    getOptions: function()
    {
        return this._options;
    },

    setOptions: function( options )
    {
        this._options = options;
    }

});

// add new methods to check the variable's type
var Type =
{
    isPlainObject: function( a ) {
        return $.isPlainObject( a );
    },
    isArray: function( a ) {
        return $.isArray( a );
    },
    isHash: function( a ) {
        return this.isArray(a) || this.isPlainObject(a);
    },
    isString: function( a ){
        return typeof(a)=='string' && isNaN(a);
    },
    isNumeric: function( a ){
        return $.isNumeric(a);
    },
    isInt: function(a){
        return $.isNumeric(a) && parseInt(a)==a;
    },
    isNull: function(a){
        return a===null;
    },
    isFunction: function(a){
        return $.isFunction(a);
    }
};

// add new methods to the String object
$.extend(String.prototype,
{
    /**
    * Upercase first letter
    * The parameter specifies if the other string chars will be lowercased
    */
    ucfirst: function(lowerCase)
    {
        var string = this;
        lowerCase && (string = string.toLowerCase());

        return string.replace(/^(.)/, function(c){return c.toUpperCase()});
    },

    strcmp: function(string2)
    {
        return this.toLowerCase().localeCompare(string2.toLowerCase());
    },

    strcasecmp: function(string2)
    {
        return this.localeCompare(string2);
    },

    strnatcmp: function(string2, version)
    {
        return this.toLowerCase().strnatcasecmp(string2.toLowerCase(), version);
    },

    strnatcasecmp: function (b)
    {
        var a = this;

        function isWhitespaceChar(a)
        {
            var charCode;
            charCode = a.charCodeAt(0);

            if ( charCode <= 32 )
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        function isDigitChar(a)
        {
            var charCode;
            charCode = a.charCodeAt(0);

            if ( charCode >= 48  && charCode <= 57 )
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        function compareRight(a,b)
        {
            var bias = 0;
            var ia = 0;
            var ib = 0;

            var ca;
            var cb;

            // The longest run of digits wins.  That aside, the greatest
            // value wins, but we can't know that it will until we've scanned
            // both numbers to know that they have the same magnitude, so we
            // remember it in BIAS.
            for (;; ia++, ib++) {
                ca = a.charAt(ia);
                cb = b.charAt(ib);

                if (!isDigitChar(ca)
                    && !isDigitChar(cb)) {
                    return bias;
                } else if (!isDigitChar(ca)) {
                    return -1;
                } else if (!isDigitChar(cb)) {
                    return +1;
                } else if (ca < cb) {
                    if (bias == 0) {
                        bias = -1;
                    }
                } else if (ca > cb) {
                    if (bias == 0)
                        bias = +1;
                } else if (ca == 0 && cb == 0) {
                    return bias;
                }
            }
        }

        var ia = 0, ib = 0;
        var nza = 0, nzb = 0;
        var ca, cb;
        var result;

        while (true)
        {
            // only count the number of zeroes leading the last number compared
            nza = nzb = 0;

            ca = a.charAt(ia);
            cb = b.charAt(ib);

            // skip over leading spaces or zeros
            while ( isWhitespaceChar( ca ) || ca =='0' ) {
                if (ca == '0') {
                    nza++;
                } else {
                    // only count consecutive zeroes
                    nza = 0;
                }

                ca = a.charAt(++ia);
            }

            while ( isWhitespaceChar( cb ) || cb == '0') {
                if (cb == '0') {
                    nzb++;
                } else {
                    // only count consecutive zeroes
                    nzb = 0;
                }

                cb = b.charAt(++ib);
            }

            // process run of digits
            if (isDigitChar(ca) && isDigitChar(cb)) {
                if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
                    return result;
                }
            }

            if (ca == 0 && cb == 0) {
                // The strings compare the same.  Perhaps the caller
                // will want to call strcmp to break the tie.
                return nza - nzb;
            }

            if (ca < cb) {
                return -1;
            } else if (ca > cb) {
                return +1;
            }

            ++ia;
            ++ib;
        }
    }
})

$.extend(Array.prototype, {
    /**
     * Searches if the array contains a value.
     * If the value id found, returns the index of the value; else returns false
     */
    search: function(value, strict)
    {
        if (typeof strict == 'undefined') {
            strict = false;
        }

        var index;
        $.each(this, function(_index){
            if ((!strict && this.valueOf() == value) ||
                (strict && this.valueOf() === value)) {
                index = _index;
                return false;
            }
        })

        if (typeof index != 'undefined') {
            return index;
        }

        return false;
    }
})